#include "stdafx.h"
#include "http_servlet.h"
#include "master_service.h"

char *var_cfg_libcrypto_path; // For OpenSSL, MbedTLS
char *var_cfg_libx509_path;	  // For MbedTLS
char *var_cfg_libssl_path;	  // For OpenSSL, MbedTLS, and PolarSSL
char *var_cfg_crt_file;		  // For OpenSSL, MbedTLS, and PolarSSL
char *var_cfg_key_file;		  // For OpenSSL, MbedTLS, and PolarSSL
char *var_cfg_key_pass;		  // For OpenSSL, MbedTLS, and PolarSSL
char *var_cfg_hotwords_file;  // hotwords file

char *var_cfg_asr_model_dir;
char *var_cfg_vad_model_dir;
char *var_cfg_lm_model_dir;
char *var_cfg_punc_model_dir;
char *var_cfg_itn_model_dir;
char *var_cfg_log_path;

acl::master_str_tbl var_conf_str_tab[] = {
	{"libcrypto_path", "", &var_cfg_libcrypto_path},
	{"libx509_path", "", &var_cfg_libx509_path},
	{"libssl_path", "", &var_cfg_libssl_path},
	{"crt_file", "", &var_cfg_crt_file},
	{"key_file", "", &var_cfg_key_file},
	{"key_pass", "", &var_cfg_key_pass},
	{"model-dir", "/home/admin/models/damo/asr", &var_cfg_asr_model_dir},
	{"punc-dir", "/home/admin/models/damo/punc", &var_cfg_punc_model_dir},
	{"vad-dir", "/home/admin/models/damo/vad", &var_cfg_vad_model_dir},
	{"lm-dir", "/home/admin/models/damo/lm", &var_cfg_lm_model_dir},
	{"itn-dir", "/home/admin/models/thuduj12/fst_itn_zh", &var_cfg_itn_model_dir},
	{"hw_file", "/home/admin/models/damo/asr/hotwords.txt", &var_cfg_hotwords_file},
	{"log_path", "/opt/soft/acl-master/var/log/", &var_cfg_log_path},
	{0, 0, 0}};

int var_cfg_ssl_session_cache;

acl::master_bool_tbl var_conf_bool_tab[] = {
	{"ssl_session_cache", 1, &var_cfg_ssl_session_cache},

	{0, 0, 0}};

static int var_cfg_io_timeout;
static int var_cfg_thread_number;

int var_cfg_max_log_size;

int var_cfg_fst_inc_wts;
int var_cfg_global_beam;
int var_cfg_lattice_beam;
int var_cfg_am_scale;

acl::master_int_tbl var_conf_int_tab[] = {
	{"io_timeout", 120, &var_cfg_io_timeout, 0, 0},
	{"thread_number", 1, &var_cfg_thread_number, 0, 0},
	{"max_log_size", 1, &var_cfg_max_log_size, 0, 0},
	{"fst_inc_wts", 20, &var_cfg_fst_inc_wts, 0, 0},
	{"global_beam", 3, &var_cfg_global_beam, 0, 0},
	{"lattice_beam", 3, &var_cfg_lattice_beam, 0, 0},
	{"am_scale", 10, &var_cfg_am_scale, 0, 0},
	{0, 0, 0, 0, 0}};

acl::master_int64_tbl var_conf_int64_tab[] = {
	{0, 0, 0, 0, 0}};

//////////////////////////////////////////////////////////////////////////

void (signal_handler)(const char* data, size_t size)
{
	acl::string error(data,size);
	LOG(ERROR) << error.c_str();
}

master_service::master_service(void)
	: conf_(NULL)
{
}

master_service::~master_service(void)
{
}

acl::sslbase_io *master_service::setup_ssl(acl::socket_stream &conn,
										   acl::sslbase_conf &conf)
{
	acl::sslbase_io *hook = (acl::sslbase_io *)conn.get_hook();
	if (hook != NULL)
	{
		return hook;
	}

	// 对于使用 SSL 方式的流对象，需要将 SSL IO 流对象注册至网络
	// 连接流对象中，即用 ssl io 替换 stream 中默认的底层 IO 过程

	// logger("begin setup ssl hook...");

	// 采用阻塞 SSL 握手方式
	acl::sslbase_io *ssl = conf.create(false);
	if (conn.setup_hook(ssl) == ssl)
	{
		LOG(ERROR) << "setup_hook error!";
		ssl->destroy();
		return NULL;
	}

	if (!ssl->handshake())
	{
		LOG(ERROR) << "ssl handshake failed";
		ssl->destroy();
		return NULL;
	}

	if (!ssl->handshake_ok())
	{
		LOG(INFO) << "handshake trying again...";
		ssl->destroy();
		return NULL;
	}

	LOG(INFO) << "handshake_ok";

	return ssl;
}

void master_service::on_accept(acl::socket_stream &conn)
{
	clock_t start, end;
	start = clock();
	connect_num++;

	LOG(INFO) << "connect from " << conn.get_peer() << ",fd " << conn.sock_handle() << ",count:" << connect_num;
	if (conf_)
	{
		acl::sslbase_io *ssl = setup_ssl(conn, *conf_);
		if (ssl == NULL)
		{
			return;
		}
	}

	conn.set_rw_timeout(120);

	http_servlet servlet(&conn, &connect_num);
	servlet.setAsrHandle(asr_handle_);
	servlet.setHotwords(std::make_shared<std::unordered_map<std::string, int>>(hws_map));

	// charset: big5, gb2312, gb18030, gbk, utf-8
	servlet.setLocalCharset("utf-8");

	while (servlet.doRun())
	{
	}

	end = clock();
	double used_sec = (double)(end - start) / CLOCKS_PER_SEC;

	connect_num--;
	LOG(INFO) << "disconnect from " << conn.get_peer() << ", fd " << conn.sock_handle() << ",used " << used_sec << ",count:" << connect_num;
}

void master_service::proc_pre_jail(void)
{
	//FLAGS_alsologtostderr = true;			   // 是否将日志输出到文件和stderr
	FLAGS_log_dir = var_cfg_log_path;		   // 设置日志文件输出目录,目录必须存在
	FLAGS_max_log_size = var_cfg_max_log_size; // 最大日志大小（MB）, 如果设置为0将默认为1

	google::SetStderrLogging(google::GLOG_WARNING); // 输出log的最低等级是 INFO (可以设置为WARNING或者更高)
	google::InstallFailureSignalHandler();     		// 配置安装程序崩溃失败信号处理器
	google::InstallFailureWriter(&signal_handler);  // 安装配置程序失败信号的信息打印过程，设置回调函数
	google::InitGoogleLogging("asr_service"); // 用当前可执行程序初始化glog

	LOG(INFO) << ">>>proc_pre_jail<<<";
}

void master_service::proc_on_listen(acl::server_socket &ss)
{
	LOG(INFO) << ">>>listen " << ss.get_addr() << "ok<<<";
}

void master_service::proc_on_init(void)
{
	LOG(INFO) << ">>>proc_on_init<<<";

	// load models
	try
	{
		// init model with api
		std::map<std::string, std::string> model_path;
		model_path.insert({"model-dir", var_cfg_asr_model_dir});
		model_path.insert({"vad-dir", var_cfg_vad_model_dir});
		model_path.insert({"punc-dir", var_cfg_punc_model_dir});
		model_path.insert({"lm-dir", var_cfg_lm_model_dir});
		model_path.insert({"itn-dir", var_cfg_itn_model_dir});
		model_path.insert({"quantize", "true"});
		model_path.insert({"vad-quant", "true"});
		model_path.insert({"punc-quant", "true"});
		/*
				model_path.insert({"model-revision", "v1.2.1"});
				model_path.insert({"vad-revision", "v1.2.0"});
				model_path.insert({"punc-revision", "v1.1.7"});
				model_path.insert({"itn-revision", "v1.0.1"});
				model_path.insert({"lm-revision", "v1.0.2"});

		*/
		asr_handle_ = FunOfflineInit(model_path, var_cfg_thread_number);

		// Extract hotword file
		acl::ifstream fileStream;
		acl::string content;
		fileStream.load(var_cfg_hotwords_file, content);
		std::vector<acl::string> hotwords;
		hotwords = content.split2("\r\r\n");
		for (size_t i = 0; i < hotwords.size(); i++)
		{
			acl::string line = hotwords[i];
			std::vector<acl::string> hotword_weight = hotwords[i].split2(" \t");
			hws_map.insert({hotword_weight[0], atoi(hotword_weight[1].c_str())});
		}

		LOG(INFO) << "load model ok";
	}
	catch (const std::exception &e)
	{
		LOG(INFO) << "error:" << e.what();
	}

	// 下面用来初始化 SSL 功能

	if (var_cfg_crt_file == NULL || *var_cfg_crt_file == 0 || var_cfg_key_file == NULL || *var_cfg_key_file == 0)
	{
		LOG(INFO) << "not use SSL mode";
		return;
	}

	if (strstr(var_cfg_libssl_path, "mbedtls"))
	{
		acl::mbedtls_conf::set_libpath(var_cfg_libcrypto_path,
									   var_cfg_libx509_path, var_cfg_libssl_path);
		if (!acl::mbedtls_conf::load())
		{
			LOG(ERROR) << "load " << var_cfg_libssl_path << "error";
			return;
		}

		LOG(INFO) << "MbedTLS loaded, crypto=" << var_cfg_libcrypto_path << ",x509=" << var_cfg_libx509_path << ",ssl=" << var_cfg_libssl_path;
		conf_ = new acl::mbedtls_conf(true);
	}
	else if (strstr(var_cfg_libssl_path, "polarssl"))
	{
		acl::polarssl_conf::set_libpath(var_cfg_libssl_path);
		if (!acl::polarssl_conf::load())
		{
			LOG(ERROR) << "load " << var_cfg_libssl_path << " error";
			return;
		}

		LOG(INFO) << "PolarSSL loaded, ssl=" << var_cfg_libssl_path;

		conf_ = new acl::polarssl_conf();
	}
	else if (strstr(var_cfg_libssl_path, "libssl"))
	{
		acl::openssl_conf::set_libpath(var_cfg_libcrypto_path,
									   var_cfg_libssl_path);
		if (!acl::openssl_conf::load())
		{
			LOG(ERROR) << "load " << var_cfg_libssl_path << " error";
			return;
		}

		LOG(INFO) << "OpenSSL loaded, crypto=" << var_cfg_libcrypto_path << ",ssl=" << var_cfg_libssl_path;

		conf_ = new acl::openssl_conf(true);
	}
	else
	{
		LOG(INFO) << "unsupported ssl=" << var_cfg_libssl_path;
		return;
	}

	// 允许服务端的 SSL 会话缓存功能
	conf_->enable_cache(var_cfg_ssl_session_cache);

	// 添加本地服务的证书及服务密钥
	if (!conf_->add_cert(var_cfg_crt_file, var_cfg_key_file,
						 var_cfg_key_pass))
	{
		LOG(ERROR) << "add cert failed, crt: " << var_cfg_crt_file << "key:" << var_cfg_key_file;
		delete conf_;
		conf_ = NULL;
		return;
	}

	LOG(INFO) << "load cert ok, crt: " << var_cfg_crt_file << ", key: " << var_cfg_key_file;
}

void master_service::proc_on_exit(void)
{
	LOG(INFO) << ">>>proc_on_exit<<<";
	// free models
	FunOfflineUninit(asr_handle_);
	delete conf_;

	google::ShutdownGoogleLogging();
}

bool master_service::proc_on_sighup(acl::string &)
{
	LOG(INFO) << ">>>proc_on_sighup<<<";
	return true;
}
